package org.example;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.example.myRateLimiter.MyRateLimiter;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;

@Aspect
public class RateLimiterAspect {

    //测试有没有注入成功
    @PostConstruct
    public void test(){
        System.out.println("RateLimiterAspect 注入成功");
    }

    @Autowired
    private MyRateLimiterContext myRateLimiterContext;

    //切点 要拦截增强的地方
    @Pointcut("@annotation(useRateLimiter)")
    public void rateLimiterMethods(UseRateLimiter useRateLimiter) {
    }


    //增强逻辑 环绕增强
    @Around("rateLimiterMethods(useRateLimiter)")
    public Object handleRateLimit(ProceedingJoinPoint joinPoint, UseRateLimiter useRateLimiter) {
        //先获取限流器 传方法(缓存) 和注解上的东西
        // 获取当前方法的签名
        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("Signature is not a MethodSignature");
        }
        MethodSignature methodSignature = (MethodSignature) signature;
        //原方法
        Method method = methodSignature.getMethod();

        //注解的name
        String name = useRateLimiter.name();

        //注解的capacity和rate
        int capacity = useRateLimiter.capacity();
        int rate = useRateLimiter.rate();

        //通过name和method 获取限流器
        MyRateLimiter myRateLimiter = myRateLimiterContext.getRateLimiter(method, name,  capacity, rate);


        // 限流逻辑 直接调用他的tryExecute方法 即可
        MyMethodInvocation myMethodInvocation = new MyMethodInvocation(method,
                joinPoint.getTarget(), joinPoint.getArgs());

        Method fallbackMethod = getFallbackMethod(joinPoint, useRateLimiter);
        MyMethodInvocation fallbackMethodInvocation = new MyMethodInvocation(fallbackMethod,
                joinPoint.getTarget(), joinPoint.getArgs());

        return myRateLimiter.tryExecute(myMethodInvocation, fallbackMethodInvocation);

    }

    //根据切点和注解上的降级方法名 返回降级方法的Method对象
    private Method getFallbackMethod(ProceedingJoinPoint joinPoint, UseRateLimiter useRateLimiter) {
        String fallbackMethodName = useRateLimiter.fallbackMethod();
        Method fallbackMethod;
        try {
            if (joinPoint.getArgs().length == 0) {
                fallbackMethod = joinPoint.getTarget().getClass().getDeclaredMethod(fallbackMethodName);
            } else {
                fallbackMethod = joinPoint.getTarget().getClass().getDeclaredMethod(fallbackMethodName,
                        joinPoint.getArgs().getClass());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        fallbackMethod.setAccessible(true);

        return fallbackMethod;

    }

}
